package org.daochong.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

public class FileUtils {

	private static Map<String, String[]> MAGIC_NUMBER = new HashMap<String, String[]>();
	private static Map<String, List<String>> TYPES = new HashMap<String, List<String>>();
	private static int MAX_MAGICNUMBER = 16;
	private static Map<String, String[]> FILE_FLAG = new HashMap<String, String[]>();

	static {
		MAGIC_NUMBER.put("jpg", new String[] { "FFD8FF" });
		MAGIC_NUMBER.put("jpeg", new String[] { "FFD8FF" });
		MAGIC_NUMBER.put("png", new String[] { "89504E47" });
		MAGIC_NUMBER.put("gif", new String[] { "47494638" });
		MAGIC_NUMBER.put("tiff", new String[] { "49492A00", "4D4D002A" });
		MAGIC_NUMBER.put("bmp", new String[] { "424D" });
		MAGIC_NUMBER.put("psd", new String[] { "38425053" });
		List<String> list = new ArrayList<String>();
		list.add("jpg");
		list.add("jpeg");
		list.add("png");
		list.add("gif");
		list.add("tiff");
		list.add("bmp");
		list.add("psd");
		TYPES.put("Image", list);

		MAGIC_NUMBER.put("dwg", new String[] { "41433130" });
		MAGIC_NUMBER.put("class", new String[] { "CAFEBABE" });

		MAGIC_NUMBER.put("doc", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("xls", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("ppt", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("dot", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("xla", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("ppa", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("pps", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("pot", new String[] { "D0CF11E0A1B11AE1" });
		MAGIC_NUMBER.put("docx", new String[] { "504B0304" });
		MAGIC_NUMBER.put("xlsx", new String[] { "504B0304" });
		MAGIC_NUMBER.put("pptx", new String[] { "504B0304" });

		MAGIC_NUMBER.put("rtf", new String[] { "7B5C727466" });
		MAGIC_NUMBER.put("xml", new String[] { "3C3F786D6C" });
		MAGIC_NUMBER.put("pdf", new String[] { "255044462D312E" });

		list = new ArrayList<String>();
		list.add("doc");
		list.add("xls");
		list.add("ppt");
		list.add("docx");
		list.add("xlsx");
		list.add("pptx");
		list.add("rtf");
		list.add("pdf");
		list.add("xml");
		TYPES.put("Doc", list);

		MAGIC_NUMBER.put("zip", new String[] { "504B0304" });
		MAGIC_NUMBER.put("jar", new String[] { "504B0304" });
		MAGIC_NUMBER.put("rar", new String[] { "52617221" });
		MAGIC_NUMBER.put("gz", new String[] { "1F8B08" });
		MAGIC_NUMBER.put("tgz", new String[] { "1F8B08" });
		MAGIC_NUMBER.put("tar", new String[] { "312E706466" });
		MAGIC_NUMBER.put("7z", new String[] { "377ABCAF271C" });

		list = new ArrayList<String>();
		list.add("zip");
		list.add("jar");
		list.add("rar");
		list.add("gz");
		list.add("tgz");
		list.add("tar");
		list.add("7z");
		TYPES.put("Zip", list);

		MAGIC_NUMBER.put("wav", new String[] { "57415645" });
		MAGIC_NUMBER.put("ram", new String[] { "2E7261FD" });
		MAGIC_NUMBER.put("mid", new String[] { "4D546864" });
		MAGIC_NUMBER.put("mp3", new String[] { "494433", "FFFD74", "FFFA51", "FFFA91", "FFFA71", "FFFA61", "FFFDB0",
				"FFFB90", "FFFB92", "FFFD74", "FFF368", "FFF374", "FFF340" });

		list = new ArrayList<String>();
		list.add("wav");
		list.add("ram");
		list.add("mid");
		TYPES.put("Audio", list);

		MAGIC_NUMBER.put("avi", new String[] { "41564920" });
		MAGIC_NUMBER.put("rm", new String[] { "2E524D46" });
		MAGIC_NUMBER.put("mov", new String[] { "6D6F6F76" });
		MAGIC_NUMBER.put("mpg", new String[] { "000001B3", "000001BA" });
		MAGIC_NUMBER.put("asf", new String[] { "3026B2758E66CF11" });
		MAGIC_NUMBER.put("wmv", new String[] { "3026B2758E66CF11" });
		MAGIC_NUMBER.put("mp4", new String[] { "00000018667479706d703432" });

		list = new ArrayList<String>();
		list.add("avi");
		list.add("rm");
		list.add("mov");
		list.add("mpg");
		list.add("asf");
		TYPES.put("Video", list);

		list = new ArrayList<String>();
		list.addAll(TYPES.get("Audio"));
		list.addAll(TYPES.get("Video"));
		TYPES.put("Media", list);

		MAGIC_NUMBER.put("elf", new String[] { "7F454C4601010100" });
		MAGIC_NUMBER.put("exe", new String[] { "4d5a" });
		MAGIC_NUMBER.put("dll", new String[] { "4d5a" });
		MAGIC_NUMBER.put("drv", new String[] { "4d5a" });
		MAGIC_NUMBER.put("vxd", new String[] { "4d5a" });
		MAGIC_NUMBER.put("sys", new String[] { "4d5a" });
		MAGIC_NUMBER.put("ocx", new String[] { "4d5a" });
		MAGIC_NUMBER.put("vbx", new String[] { "4d5a" });

		list = new ArrayList<String>();
		list.add("elf");
		list.add("exe");
		list.add("dll");
		list.add("drv");
		list.add("vxd");
		list.add("sys");
		list.add("ocx");
		list.add("vbx");
		TYPES.put("EXEC", list);

		MAGIC_NUMBER.put("mdb", new String[] { "5374616E64617264204A" });
		MAGIC_NUMBER.put("eml", new String[] { "44656C69766572792D646174653A" });
		MAGIC_NUMBER.put("dwg", new String[] { "41433130" });
		MAGIC_NUMBER.put("dbx", new String[] { "CFAD12FEC5FD746F" });
		MAGIC_NUMBER.put("pst", new String[] { "2142444E" });
		MAGIC_NUMBER.put("wpd", new String[] { "FF575043" });
		MAGIC_NUMBER.put("eps", new String[] { "252150532D41646F6265" });
		MAGIC_NUMBER.put("qdf", new String[] { "AC9EBD8F" });
		MAGIC_NUMBER.put("pml", new String[] { "E3828596" });
		MAGIC_NUMBER.put("pst", new String[] { "2142444E" });
		MAGIC_NUMBER.put("pst", new String[] { "2142444E" });
		MAGIC_NUMBER.put("pst", new String[] { "2142444E" });

		FILE_FLAG.put("jsp", new String[] { "<jsp:", "<%" });
		FILE_FLAG.put("php", new String[] { "<?php" });
		FILE_FLAG.put("asp", new String[] { "<%" });
	}

	public static boolean saveToFile(OutputStream out, byte[] content) {
		try {
			out.write(content);
			out.close();
			return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean saveToFile(String path, byte[] content) {
		try {
			return saveToFile(new FileOutputStream(path), content);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean saveToFile(String path, String content) {
		return saveToFile(path, content, null);
	}

	public static boolean saveToFile(String path, String content, String encode) {
		try {
			if (encode != null) {
				return saveToFile(new FileOutputStream(path), content.getBytes(encode));
			} else {
				return saveToFile(new FileOutputStream(path), content.getBytes());
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static void register(String ext, String magicNumber) {
		register(ext, magicNumber, null);
	}

	public static void register(String ext, String magicNumber, String type) {
		if (StringUtils.isEmpty(ext) || StringUtils.isEmpty(magicNumber)) {
			return;
		}
		if (MAGIC_NUMBER.containsKey(ext)) {
			String[] ar = MAGIC_NUMBER.get(ext);
			for (String s : ar) {
				if (s.equals(magicNumber)) {
					return;
				}
			}
			String[] nar = new String[ar.length + 1];
			for (int i = 0; i < ar.length; i++) {
				nar[i] = ar[i];
			}
			nar[ar.length] = magicNumber;
			MAGIC_NUMBER.put(ext, nar);
		} else {
			MAGIC_NUMBER.put(ext, new String[] { magicNumber });
		}
		if (type != null) {
			List<String> list = TYPES.get(type);
			if (list == null) {
				list = new ArrayList<String>();
			}
			list.add(ext);
		}
	}

	public static List<String> supportExts() {
		List<String> list = new ArrayList<String>();
		list.addAll(MAGIC_NUMBER.keySet());
		return list;
	}

	public static boolean support(String ext) {
		if (isEmpty(ext)) {
			return false;
		}
		ext = ext.toLowerCase();
		return MAGIC_NUMBER.containsKey(ext);
	}

	public static boolean check(File file) {
		try {
			return check(getExt(file), new FileInputStream(file));
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	public static boolean checkImage(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkImage(data);
	}

	public static boolean checkImage(byte[] data) {
		return checkType("Image", data);
	}

	public static boolean checkDoc(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkDoc(data);
	}

	public static boolean checkDoc(byte[] data) {
		return checkType("Doc", data);
	}

	public static boolean checkZip(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkZip(data);
	}

	public static boolean checkZip(byte[] data) {
		return checkType("Zip", data);
	}

	public static boolean checkAudio(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkAudio(data);
	}

	public static boolean checkAudio(byte[] data) {
		return checkType("Audio", data);
	}

	public static boolean checkVideo(byte[] data) {
		return checkType("Media", data);
	}

	public static boolean checkVideo(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkVideo(data);
	}

	public static boolean checkMedia(byte[] data) {
		return checkType("Media", data);
	}

	public static boolean checkMedia(InputStream in) {
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return checkMedia(data);
	}

	public static boolean checkType(String type, byte[] data) {
		List<String> list = TYPES.get(type);
		if (list == null)
			return false;
		for (String ext : list) {
			if (!support(ext)) {
				throw new UnsupportedException(ext);
			}
			String[] ar = MAGIC_NUMBER.get(ext);
			for (String str : ar) {
				int len = str.length() / 2;
				byte[] d = data;
				if (len < data.length) {
					d = Arrays.copyOf(data, len);
				}
				String fp = hexEncode(d);
				if (str.equalsIgnoreCase(fp)) {
					return true;
				}
			}
		}
		return false;
	}

	public static boolean check(String ext, InputStream in) {
		if (!support(ext)) {
			throw new UnsupportedException(ext);
		}
		byte[] data = new byte[MAX_MAGICNUMBER];
		try {
			in.read(data);
			in.close();
			return check(ext, data);
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

	}

	public static boolean check(String ext, byte[] data) {
		if (!support(ext)) {
			throw new UnsupportedException(ext);
		}
		ext = ext.toLowerCase();
		String[] ar = MAGIC_NUMBER.get(ext);
		for (String str : ar) {
			int len = str.length() / 2;
			if (len < data.length) {
				data = Arrays.copyOf(data, len);
			}
			if (str.equalsIgnoreCase(hexEncode(data))) {
				return true;
			}
		}
		return false;
	}

	public static String getExt(File file) {
		try {
			String name = file.getName();
			int pos = name.lastIndexOf('.');
			if (pos == -1) {
				name = "";
			} else {
				name = name.substring(pos + 1).trim();
			}
			return name;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	public static boolean isEmpty(String s) {
		return (s == null ? true : s.trim().equals(""));
	}

	public static List<String> getFileExt(File file) {
		try {
			if (file == null || file.isDirectory() || !file.exists()) {
				return new ArrayList<String>();
			}
			InputStream in = new FileInputStream(file);
			byte[] data = new byte[MAX_MAGICNUMBER];
			in.read(data);
			in.close();
			List<String> re = getFileExt(data);
			if (re.size() == 0) {

			}
			return re;
		} catch (Throwable e) {

		}
		return new ArrayList<String>();
	}

	public static List<String> getFileExt(InputStream in) {
		try {
			byte[] data = new byte[MAX_MAGICNUMBER];
			in.read(data);
			in.close();
			return getFileExt(data);
		} catch (Throwable e) {

		}
		return new ArrayList<String>();
	}

	public static List<String> getFileExt(byte[] data) {
		List<String> list = new ArrayList<String>();
		for (String ext : MAGIC_NUMBER.keySet()) {
			for (String str : MAGIC_NUMBER.get(ext)) {
				int len = str.length() / 2;
				if (str.equalsIgnoreCase(StringUtils.hexEncode(data, len))) {
					list.add(ext);
				}
			}
		}
		return list;
	}

	private static final char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
			'f' };

	public static String hexEncode(byte dataStr[]) {
		StringWriter w = new StringWriter();
		for (int i = 0; i < dataStr.length; i++) {
			int b = dataStr[i];
			w.write(hex[b >> 4 & 0xf]);
			w.write(hex[b >> 0 & 0xf]);
		}
		return w.toString();
	}

	public static boolean checkFileContent(String type, InputStream in) {
		boolean re = false;
		try {
			if ("Image".equalsIgnoreCase(type)) {
				try {
					re = ImageIO.read(in) != null;
					return true;
				} catch (Throwable e) {
					return false;
				}
			}
			byte[] data = new byte[MAX_MAGICNUMBER];
			in.read(data);
			in.close();
			if ("Audio".equalsIgnoreCase(type)) {
				re = checkAudio(data);
			} else if ("Video".equalsIgnoreCase(type)) {
				re = checkVideo(data);
			} else if ("Media".equalsIgnoreCase(type)) {
				re = checkMedia(data);
			} else {
				re = checkImage(data) || checkDoc(data) || checkZip(data);
			}

		} catch (Throwable e) {

		}
		return re;
	}

	public static boolean checkFileContent(String type, File file) {
		boolean re = false;
		try {
			InputStream in = new FileInputStream(file);
			return checkFileContent(type, in);
		} catch (Throwable e) {

		}
		if (!re) {
			file.delete();
		}
		return re;
	}

	public static boolean validataPath(String str) {
		if (StringUtils.isEmpty(str))
			return false;
		
		return false;
	}
}
